今天向大家介绍c++20核心语法modules,从c++11到今天的c++20,这是c++不断推陈出新从量变到质变的过程。在介绍modules之前,我们先来了解include机制。
include机制
c++为了完全兼容c语言,让c语言开发者快速学习c++语言,因此继承了c语言的include机制。include是包含的意思,就是将头文件的所有内容复制过来,#则代表预处理的意思,相关的预处理还有#ifdef、#endif,在此就不做过多的介绍。
c++将代码分为头文件和cpp源文件,头文件主要是申明函数等相关接口,cpp文件则是具体实现。开发者可以将cpp文件编译成lib文件,使用者无需关系其具体的实现,这样能很好的将实现逻辑和接口分离。
什么是编译单元
当一个c或cpp文件在编译时,预处理器首先递归包含头文件,形成一个含有所有必要信息的单个源文件,这个源文件就是一个编译单元。
编译单元和头文件
在同一个编译单元里,重复包含头文件会照成重复定义的问题,例如:一个头文件定义了一个class(不是申明),然后在一个cpp文件中包含两次,编译器就会报“类型重定义”错误。为了解决这一编译错误问题,必须要在头文件里写上#ifndef、#define 和 #endif(新版编译器你可以#pragma once),但大多数人认为这一解决办法并不完美。
编译真的很慢很慢
c++编译慢是公认的,你会发现编译一个工程的时间足够干很多事情,其中有一大部分原因就是include照成的。一个公共的头文件很可能被N个编译单元包含,这N个编译单元都会去编译头文件,尤其是修改这个公共头文件,再编译简直让人欲哭无泪,准确的诠释了“牵一发而动全身”。
可以看出include的机制有一系列问题,这些问题总是不尽人意,曾经有多少人抱怨include机制太烂,又有多少人为此转移阵营而投靠其它语言。面对标准委员会的一次又一次推移,最终module特性冻结在c++20。
modules重磅来袭
C++不适应在大规模程序设计与现代开发中的应用环境,越来越多的模板的使用,已经导致了编译时可伸缩性和程序员生产力的严重障碍。它构建性能低下,与云和分布式构建的集成性差。此外,严重依赖头文件包含(即从编译器的角度复制代码)和宏扼杀了C++开发人员的开发。
为了解决以上系列问题,c++20 modules应运而生!(手动特效:金光闪闪)
其中,WG21官方的n4465.pdf文档对modules的目标描述:
1. componentization;
2. isolation from macros;
3. scalable build;
4. support for modern semantics-aware developer tools.
gcc官方对其描述:
Reduce build times due to not reparsing large header files
Proper interface/implementation isolation
Harder to have ODR violations
mvc、gcc、clang 三大编译器基本已完成c++17的开发工作,目前正在开发c++20核心功能。gcc编译器很早就建立了module分支,可以从gcc官网看到,2017年3月1日第一个模块可执行文件运行。mvc已经开始试验性的支持module,可以从微软官方c++博客看到,vs2015就开始支持modules了,我相信正式版本将很快与我们见面。
开始modules工作
如果您使用的是Visual Studio 2017 15.3之前的版本,请添加/experimental:module /module:stdIfcDir "$(VCToolsInstallDir_150)ifc\$(PlatformTarget)"到`` 配置属性''-> `` C / C ++''->``命令行''以打开该项目的模块。
(我的版本是vs2017 15.9.7 ,所以我直接跳过上一步)接下来,项目属性页->c/c++->语言->c++语言标准选择“最新草案标准(/std:c++latest)”,启用c++模块(实验性)选择“是”。
修改属性页
//#include <vector> import std.core; int main() { std::vector vec; vec.push_back(1); vec.push_back(2); vec.push_back(3); for (auto it = vec.begin(); it != vec.end(); ++it) { printf("%d\n", *it); } system("pause"); return 0; }
以上是stl的modules示例,微软官方提供的std模块参考如下
std.regex 提供标题的内容
std.filesystem 提供标题的内容
std.memory 提供标题的内容
std.threadingprovodes头部的内容
std.core 提供C ++标准库中的所有其他内容
自定义模块
新建Mo.ixx文件(注意后缀名的变化),然后我们需要在main函数前导入MO模块(import MO;)并使用它,需要注意的是只有标记export的才会被导出,否则外部无法调用。我们可以在模块中导入子模块,可以用大括号批量export,也可以更小粒度的控制export。
export module MO; import std.core; export { void test() { printf("test。。。"); } namespace SPACE { class File { public void process(); }; } } void test2() { printf("test2。。。"); } void SPACE::File::process() { }
编译它会生成两个文件,分别是obj和ifc。obj我们并不陌生,那么ifc是个什么东西?其实它是模块的二进制描述文件,无法手动更改,虽然没有了头文件,但是需要一个ifc文件,让编译器知道接口信息,用于和其它TU建立桥梁。可以看到模块直接被当做TU处理,无论你有多少个编译单元import该模块,都不会重复编译,大大提高了编译速度,是不是很酷!
c++20 module 将是一个全新的开始,所带来改变是巨大的,这些改变是不言而喻的,从这一刻开始构建你的大型项目。好吧,我们扔掉include,从modules开始,更快的构建速度,更清爽的代码架构,更小的功能依赖,更好的独立组件,像c#一样高效而优雅的开发。
结束语
modules终于来了!模块对于开发的好处毋庸置疑,我相信这是很多人都有所期待的,那些抱怨include垃圾而转移阵营的同学,是时候回归c++了。
从c++11到c++20,还在用c++98的你,我就问你慌不慌?当下急速发展的互联网时代,不学习就意味着淘汰,希望大家勇于接受新事物!
本页共124段,4113个字符,7407 Byte(字节)